import sys, os
from paraview import servermanager
import SOAPpy
from SOAPpy import WSDL
from threading import Thread
import time
import socket
from math import sqrt, acos, cos, sin, ceil
from paraview import vtk

class Vector:
	def Length( vector ):
		return sqrt(vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2])

	def Normalize( vec ):
		length = Vector.Length(vec)
		vec[0] = vec[0] / length
		vec[1] = vec[1] / length
		vec[2] = vec[2] / length
		
		return vec

	def Cross( vec1, vec2 ):
		return [( vec1[1] * vec2[2] ) - ( vec1[2] * vec2[1] ), 
	  			( vec1[2] * vec2[0] ) - ( vec1[0] * vec2[2] ), 
	  			( vec1[0] * vec2[1] ) - ( vec1[1] * vec2[0] ) ]
   
	def Dot( vec1, vec2 ):
		return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]

	def FindLeastParallelAxis( vec ):
		dotX = Vector.Dot(vec, [1, 0, 0])
		dotY = Vector.Dot(vec, [0, 1, 0])
		dotZ = Vector.Dot(vec, [0, 0, 1])

		if dotX <= dotY:
			if dotX <= dotZ:
				return [1, 0, 0]
			elif dotY <= dotZ:
				return [0, 1, 0]
			
		return [0, 0, 1]
   
	def Substract( vec1, vec2 ):
		return [ vec1[0] - vec2[0], vec1[1] - vec2[1], vec1[2] - vec2[2] ]

	def Add( vec1, vec2 ):
		return [vec1[0]+vec2[0], vec1[1]+vec2[1], vec1[2]+vec2[2]]

	def Multiply( vec, scalar ):
		return [vec[0] * scalar, vec[1] * scalar, vec[2] * scalar ]

	def ProjectOntoPlane( vec, normal ):
		# normalize normal
		Vector.Normalize(normal)
		
		# projected = V - (V . Normal)*N
		return Vector.Substract( vec, Vector.Multiply( normal, Vector.Dot( vec,  normal ) ) )

	def MultiplyVectors(vec1, vec2):
		return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]
	
	def GetEyeVector(center, normal):
		return[center[0]+normal[0]*center[0], center[1]+normal[1]*center[1], center[2]+normal[2]*center[2]];
	
	def GetDirectionCosines( normal ):
		leastParallelAxis = Vector.FindLeastParallelAxis( normal )
		projectedXAxis = Vector.ProjectOntoPlane( leastParallelAxis, normal )
		projectedYAxis = Vector.Cross( projectedXAxis, normal )
		Vector.Normalize(projectedYAxis)
		
		return [projectedXAxis[0], projectedXAxis[1], projectedXAxis[2],
				projectedYAxis[0], projectedYAxis[1], projectedYAxis[2],
				normal[0], normal[1], normal[2]]


	GetDirectionCosines = staticmethod(GetDirectionCosines)
	FindLeastParallelAxis = staticmethod(FindLeastParallelAxis)
	Dot = staticmethod(Dot)
	Normalize = staticmethod(Normalize)
	ProjectOntoPlane = staticmethod(ProjectOntoPlane)
	Multiply = staticmethod(Multiply)
	Substract = staticmethod(Substract)
	Cross = staticmethod(Cross)
	Length = staticmethod(Length)
	GetEyeVector = staticmethod(GetEyeVector)
	MultiplyVectors = staticmethod(MultiplyVectors)
	
class Heartbeat(Thread):
	__serverId = None

	def run(self):
		while 1:
			time.sleep(10)
			try:
				id = proxy.ping()
				if self.__serverId == None:
					self.__serverId = id
				elif self.__serverId != id:
					print "Detected different serverId, stopping SOAP server"
					os._exit(1)
			except:
				print "- "+str(sys.exc_info())
				print "Heartbeat failed, stopping SOAP server"
				os._exit(1)

def moveParallelCamera(camera, xMove, yMove):
	camPos = camera.GetPosition()
	camFP = camera.GetFocalPoint()
	camera.SetPosition(camPos[0]+xMove, camPos[1]+yMove, camPos[2])
	camera.SetFocalPoint(camFP[0]+xMove, camFP[1]+yMove, camFP[2])

def moveParallelCameraToImagePosition(camera, xPos, yPos, maxViewDimension):
	camPos = camera.GetPosition()
	camFP = camera.GetFocalPoint()
	xP = xPos+(maxViewDimension/2)
	yP = yPos-(maxViewDimension/2)
	camera.SetPosition(xP, yP, camPos[2])
	camera.SetFocalPoint(xP, yP, camFP[2])

class ParaviewServerObject:
	__sessionId = -1
	__connection = None
	__pm = None				# processmodule object for progress events
	__alg = None
	__prog = None
	
	__transformModule = None
	__alignedReaders = []
	__annotationCutters = []
	__annotationRepresentations = []
	__annotationTransformations = []
	__annotationRepresentationOutlines = []
	__annotationLabels = []

	def __init__(self):
		if not servermanager.ActiveConnection:
		    self.__connection = servermanager.Connect()

		self.__pm = servermanager.vtkProcessModule.GetProcessModule()
		self.__pm.AddObserver("ProgressEvent", self.progressObserver)

		# the module with transform proxies is not exposed yet
		self.__transformModule = servermanager.createModule("transforms")

		return

	def progressObserver(self, caller, event):
		if(self.__pm.GetLastProgress() != self.__prog or
			self.__pm.GetLastProgressName() != self.__alg):

			self.__prog = self.__pm.GetLastProgress()
			self.__alg = self.__pm.GetLastProgressName()	

			# do a reverse soap call to the gateway
			try:
				proxy.setSessionProgress(self.__sessionId, self.__alg, self.__prog)
			except:
				# do nothing
				print ""
				# print "setJobProgress failed, is method available on the gateway?"
				
		return

	def errorEventObserver(self, caller, event):
		print self
		print caller
		print event
		return

############################################################################################

	def ping(self):
		return "Pong"

	def quit(self):
		print "Closing on server request"
		os._exit(0)

	def setupSession(self, sessionId):
		self.__sessionId = sessionId
		return "+ Session registered: "+str(sessionId)

	def setupSource(self, sourceScript):
		try:
			#print pipelineScript
			# run the script within the scope of this class
			exec(sourceScript, globals(), self.__dict__)

			return "+ "+str(len(sourceScript))+" chars executed"
		except:
			return "- "+str(sys.exc_info())
	
	def setupPipeline(self, pipelineScript):
		try:
			#print pipelineScript
			# run the script within the scope of this class
			exec(pipelineScript, globals(), self.__dict__)

			return "+ "+str(len(pipelineScript))+" chars executed"
		except:
			return "- "+str(sys.exc_info())
	
	def setupAnnotations(self, annotationFiles, annotationLabels):
#		self.annotationView = servermanager.CreateRenderView()
		try:
			if self.cutplane == None:
				return "+ No cutplane available for annotations"
				# cutplane variable present, but not defined '
		except:
			# self.cutplane variable was not defined
			return "+ No cutplane available for annotations"
		
		if len(annotationFiles) != len(annotationLabels):
			return "- The amount of annotation files and annotation labels should be equal"
			
		try:
			for i in range(0, len(annotationFiles)):
				self.__annotationLabels.append(annotationLabels[i])
				self.__annotationReaders.append(servermanager.sources.plyreader(FileName=annotationFiles[i]))
				self.__annotationCutters.append(servermanager.filters.Cut(Input=self.__annotationReaders[i]))
				self.__annotationCutters[i].CutFunction = self.cutplane
				self.__annotationCutters[i].UpdatePipeline()
	
				self.__annotationTransformations.append(servermanager.filters.TransformFilter(Input = self.__annotationCutters[i]))

				# set up transformation to 2D plane
				resliceAxes = vtk.vtkMatrix4x4()
				resliceAxes.DeepCopy([self.cutplaneDirectionCosines[0],self.cutplaneDirectionCosines[3],self.cutplaneDirectionCosines[6],self.cutplane.Origin.GetData()[0],self.cutplaneDirectionCosines[1], self.cutplaneDirectionCosines[4], self.cutplaneDirectionCosines[7],self.cutplane.Origin.GetData()[1],self.cutplaneDirectionCosines[2],self.cutplaneDirectionCosines[5],self.cutplaneDirectionCosines[8],self.cutplane.Origin.GetData()[2], 0, 0, 0, 1])
				invertedMatrix = vtk.vtkMatrix4x4() 
				vtk.vtkMatrix4x4.Invert(resliceAxes, invertedMatrix)
				
				matrix = [invertedMatrix.GetElement(0,0),invertedMatrix.GetElement(0,1),invertedMatrix.GetElement(0,2),invertedMatrix.GetElement(0,3),invertedMatrix.GetElement(1,0),invertedMatrix.GetElement(1,1),invertedMatrix.GetElement(1,2),invertedMatrix.GetElement(1,3),invertedMatrix.GetElement(2,0),invertedMatrix.GetElement(2,1),invertedMatrix.GetElement(2, 2),invertedMatrix.GetElement(2,3),invertedMatrix.GetElement(3, 0),invertedMatrix.GetElement(3,1),invertedMatrix.GetElement(3,2),invertedMatrix.GetElement(3,3)] 
				
				self.__annotationTransformations[i].Transform = self.__transformModule.Transform(Matrix=matrix)
						
				#self.__annotationRepresentations.append(servermanager.CreateRepresentation(self.__annotationTransformations[i], self.annotationView))
				self.__annotationRepresentations.append(servermanager.CreateRepresentation(self.__annotationTransformations[i], self.view))
			
				self.__annotationRepresentations[i].Representation = 2		# 2= surface, 5 = surface+edges
				self.__annotationRepresentations[i].Color = [1, 1, 1]
				#self.__annotationRepresentations[i].LineWidth = 2
				self.__annotationTransformations[i].UpdatePipeline()

				# determine the position of the label

				tmp = servermanager.Fetch(self.__annotationTransformations[i])
				print "fetch done"
				if tmp.GetNumberOfCells() > 0:
				
					# naive solution: top exactly the same as highest point, left of the label same as the right point of the annotation
					
					top = None
					left = None
					
					for j in range(0, tmp.GetNumberOfCells()):		
						tmpTuple = tmp.GetCell(j).GetPoints().GetData().GetTuple3(0)
	
						if left == None or tmpTuple[0] > left:
							left = tmpTuple[0]
						
						if top == None or tmpTuple[1] > top:
							top = tmpTuple[1]
						
						
						#tmpLines.append(tmpTuple[0])
						#tmpLines.append(tmpTuple[1])
						#tmpLines.append(tmpTuple[2])
		
						tmpTuple = tmp.GetCell(j).GetPoints().GetData().GetTuple3(1)
						if left == None or tmpTuple[0] > left:
							left = tmpTuple[0]
	
						if top == None or tmpTuple[1] > top:
							top = tmpTuple[1]
							
						#tmpLines.append(tmpTuple[0])
						#tmpLines.append(tmpTuple[1])
						#tmpLines.append(tmpTuple[2])
					
					print "top = "+str(top)+", left = "+str(left)
					
					#t1 = servermanager.sources.TextSource(Text='Cone')
					#self.__annotationRepresentationOutlines.append(servermanager.CreateRepresentation(t1, self.view, proxyName="TextSourceRepresentation"))
					t = servermanager.sources.VectorText()
					t.Text = self.__annotationLabels[i]+'dit is een hele lange test zin'
					t.UpdatePipeline()
					annotationScale = 10
					annotationHeight = (t.GetDataInformation().DataInformation.GetBounds()[3] - t.GetDataInformation().DataInformation.GetBounds()[2])*annotationScale
					
					#print(t.GetDataInformation().DataInformation)
					self.__annotationRepresentationOutlines.append(servermanager.CreateRepresentation(t, self.view))
					self.__annotationRepresentationOutlines[i].Scale = [annotationScale, annotationScale, annotationScale]
					if left != None and top != None:
						self.__annotationRepresentationOutlines[i].Position = [left, top-annotationHeight, 0]
							
				#print(dir(self.__annotationRepresentationOutlines[i]))
#				self.__annotationRepresentationOutlines.append(servermanager.CreateRepresentation(self.__annotationTransformations[i], self.view))
#				self.__annotationRepresentationOutlines[i].Representation = 3		# 3 = outline
#				self.__annotationRepresentationOutlines[i].Color = [0, 0, 1]
#				self.__annotationRepresentationOutlines[i].Opacity = 0.4
#				#self.__annotationRepresentationOutlines[i].LineWidth = 2

				

			#self.view.InteractiveRender()
			#self.view.ResetCamera()

			return "+ annotations initialized"
		except:
			return "- "+str(sys.exc_info())
	
	def getAnnotationGeometry(self, annotationIndex):
		try:
			if len(self.__annotationCutters) <= annotationIndex:
				# Should later be removed !!!
				if len(self.__annotationCutters) == 0:
					return "[]"
			
				return '- Invalid annotation index: '+str(annotationIndex)+', '+str(len(self.__annotationCutters))+' annotations available';
			
			# fetch data
			tmp = servermanager.Fetch(self.__annotationTransformations[annotationIndex])

			tmpLines = []
			for i in range(0, tmp.GetNumberOfCells()):		
				tmpTuple = tmp.GetCell(i).GetPoints().GetData().GetTuple3(0)
				tmpLines.append(tmpTuple[0])
				tmpLines.append(tmpTuple[1])
				tmpLines.append(tmpTuple[2])

				tmpTuple = tmp.GetCell(i).GetPoints().GetData().GetTuple3(1)
				tmpLines.append(tmpTuple[0])
				tmpLines.append(tmpTuple[1])
				tmpLines.append(tmpTuple[2])

			return str(tmpLines)

		except:
			return "- "+str(sys.exc_info())

	def getParameters(self, parameterGetters):
		try:
			pvs = []
			for i in range(0, len(parameterGetters)):
				v = "?????"
				try:
					if len(str(parameterGetters[i])) > 0:
						v = str(eval(str(parameterGetters[i]), globals(), self.__dict__))
						
					pvs.append(v)
				except:
					print "Parameter getter "+str(parameterGetters(i))+" could not be evaluated"
					pvs.append(v)

			if len(pvs) == 0:
				return "- No parameters requested"

			return SOAPpy.arrayType(pvs)
		except:
			return "- "+str(sys.exc_info())
	
	def setParameters(self, parameterSetters, setterTypes, parameterValues):
		try:
			if len(parameterSetters) != len(setterTypes) or len(setterTypes) != len(parameterValues):
				return "- Sizes of input arrays need to be equal"	

			for i in range(0, len(parameterSetters)):
				if len(parameterSetters[i]) > 0:

					if setterTypes[i].upper() == 'ASSIGNMENT':
						try:
							exec(str(parameterSetters[i])+" = "+str(parameterValues[i]), globals(), self.__dict__)
						except:
							return "- Assignment failed: "+str(sys.exc_info())
							
					elif setterTypes[i].upper() == 'FUNCTIONCALL':
						try:
							func = eval(parameterSetters[i], globals(), self.__dict__)
							if len(parameterValues[i]) > 0:
								value = eval(parameterValues[i], globals(), self.__dict__)
								func(value)
							else:
								func()
						except:
							return "- Setter failed:"+str(sys.exc_info())
					elif setterTypes[i].upper() == 'CUSTOMCODE':
						try:
							# ignore parameterValues
							exec(parameterSetters[i], globals(), self.__dict__)
						except:
							return "- Custom setter script failed: "+str(sys.exc_info())
					else:
						print "Setter type not supported: "+setterTypes[i]


#			proxy.setSessionProgress(self.__sessionId, "Pipeline initialized", 100)						
			
			return "+ setParameters done"
		except:
			print "Something went wrong: "+str(sys.exc_info())
			return "- "+str(sys.exc_info())
	
	def runOutput(self, outputScript):
		try:
			#print outputScript
			# run the script within the scope of this class
			exec(outputScript, globals(), self.__dict__)
			
			#proxy.setJobProgress(self.__sessionId, "Running", 100)						
				
			return "+ "+str(len(outputScript))+" chars executed"
		except:
			return "- "+str(sys.exc_info())	
	
#Create a proxy. You can call methods that are on a distant machine as if they were
#on your local machine, as if they were implemented in the proxy object.
try:
	WSDLFile   = "http://localhost:8080/ViewerServer/services/Viewer?wsdl"
	proxy      = WSDL.Proxy(WSDLFile)
	Heartbeat().start()
	
except:
	sys.exit("Could not create proxy to gateway, is the gateway running? (The error was "+str(sys.exc_info())+")")

server = SOAPpy.SOAPServer((socket.gethostname(), 0))

print "SOAP@"+str(server.server_address[0])+":"+str(server.server_address[1])

#server.config.dumpSOAPOut = 1
#server.config.dumpSOAPIn = 1

server.registerObject(ParaviewServerObject())

try:
	server.serve_forever()
	
except:
	pass
	
print "Server stopped";







				# x0 -> x3		
				#resliceAxes.SetElement(0,0,self.cutplaneDirectionCosines[0]);
				#resliceAxes.SetElement(1,0,self.cutplaneDirectionCosines[1]);
				#resliceAxes.SetElement(2,0,self.cutplaneDirectionCosines[2]);
				#resliceAxes.SetElement(3,0,0);
				# y0 -> y3
				#resliceAxes.SetElement(0,1,self.cutplaneDirectionCosines[3]);
				#resliceAxes.SetElement(1,1,self.cutplaneDirectionCosines[4]);
				#resliceAxes.SetElement(2,1,self.cutplaneDirectionCosines[5]);
				#resliceAxes.SetElement(3,1,0);
				#z0 -> z3
				#resliceAxes.SetElement(0,2,self.cutplaneDirectionCosines[6]);
				#resliceAxes.SetElement(1,2,self.cutplaneDirectionCosines[7]);
				#resliceAxes.SetElement(2,2,self.cutplaneDirectionCosines[8]);
				#resliceAxes.SetElement(3,2,0);			
				# origin (x, y, z)		
				#resliceAxes.SetElement(0,3,self.cutplane.Origin.GetData()[0]);
				#resliceAxes.SetElement(1,3,self.cutplane.Origin.GetData()[1]);
				#resliceAxes.SetElement(2,3,self.cutplane.Origin.GetData()[2]);
				#resliceAxes.SetElement(3,3,1);
	
